local ver = "$Revision: 360 $"
local print = function(a) ChatFrame1:AddMessage("|cff33ff99oUF/Jerry:|r "..tostring(a)) end
local error = function(...) print("|cffff0000Error:|r "..string.format(...)) end
local dummy = function() end

-- Colors
local colors = {
	power = {
		MANA = { r = 48/255, g = 113/255, b = 191/255},
		RAGE = { r = 226/255, g = 45/255, b = 75/255},
		FOCUS = { r = 255/255, g = 178/255, b = 0},
		ENERGY = { r = 1, g = .85, b = .1},
		HAPPINESS = { r = 0, g = 1, b = 1},
		RUNES = { r = .5, g = .5, b = .5},
		RUNIC_POWER = { r = 0, g = .82, b = 1},
		AMMOSLOT = { r = .8, g = .6, b = 0},
		FUEL = { r = 0, g = .55, b = .5},
	},
	health = {
		[0] = {r = 49/255, g = 207/255, b = 37/255}, -- Health
		[1] = {r = .6, g = .6, b = .6} -- Tapped targets
	},
	happiness = {
		[1] = {r = 1, g = 0, b = 0}, -- need.... | unhappy
		[2] = {r = 1 ,g = 1, b = 0}, -- new..... | content
		[3] = {r = 0, g = 1, b = 0}, -- colors.. | happy
	},
}

colors.power[0] = colors.power.MANA
colors.power[1] = colors.power.RAGE
colors.power[2] = colors.power.FOCUS
colors.power[3] = colors.power.ENERGY
colors.power[4] = colors.power.HAPPINESS
colors.power[5] = colors.power.RUNES
colors.power[6] = colors.power.RUNIC_POWER

-- add-on object
local oUF = {}
local callback, units, objects = {}, {}, {}

local scripts, handlers = {}, {}

local	_G, select, type, pairs, tostring, math_modf =
		_G, select, type, pairs, tostring, math.modf
local	UnitExists, UnitName, UnitInRange =
		UnitExists, UnitName, UnitInRange

local subTypes, updateTypes = {}, {}

-- Events
function scripts:OnEvent(event, ...)
	if not self:IsShown() then return end
	local handler = self[event]
	if handler then
		handler(self, event, ...)
	else
		error("Unhandled %s %s %s", self:GetName(), self.unit, event)
	end
end

function scripts:OnAttributeChanged(name, value)
	if(name == "unit" and value) then
		units[value] = self

		if(self.unit and self.unit == value) then
			return
		else
			self.unit = value
			self.id = value:match"^.-(%d+)"
			self:FullUpdate()
		end
	end
end

-- Updates
-- updating of range.


-- updating of "invalid" units.
local OnTargetUpdate = function(self, elapsed)
	if(not self.unit) then
		return
	elseif(not self.timer) then
		self.timer = .5
	elseif(self.timer <= .5) then
		self:FullUpdate()
		self.timer = .5
	else
		self.timer = self.timer - elapsed
	end
end

local BlizzardFrameHide = {
	player = function ()
		PlayerFrame:UnregisterAllEvents()
		PlayerFrame.Show = dummy
		PlayerFrame:Hide()

		PlayerFrameHealthBar:UnregisterAllEvents()
		PlayerFrameManaBar:UnregisterAllEvents()
	end,
	pet = function ()
		PetFrame:UnregisterAllEvents()
		PetFrame.Show = dummy
		PetFrame:Hide()

		PetFrameHealthBar:UnregisterAllEvents()
		PetFrameManaBar:UnregisterAllEvents()
	end,
	target = function ()
		TargetFrame:UnregisterAllEvents()
		TargetFrame.Show = dummy
		TargetFrame:Hide()

		TargetFrameHealthBar:UnregisterAllEvents()
		TargetFrameManaBar:UnregisterAllEvents()
		TargetFrameSpellBar:UnregisterAllEvents()

		ComboFrame:UnregisterAllEvents()
		ComboFrame.Show = dummy
		ComboFrame:Hide()
	end,
	targettarget = function ()
		TargetofTargetFrame:UnregisterAllEvents()
		TargetofTargetFrame.Show = dummy
		TargetofTargetFrame:Hide()

		TargetofTargetHealthBar:UnregisterAllEvents()
		TargetofTargetManaBar:UnregisterAllEvents()
	end,
	party = function ()
		for i=1,4 do
			local party = "PartyMemberFrame"..i
			local frame = _G[party]

			frame:UnregisterAllEvents()
			frame.Show = dummy
			frame:Hide()

			_G[party..'HealthBar']:UnregisterAllEvents()
			_G[party..'ManaBar']:UnregisterAllEvents()
		end
	end,
}

local HandleUnit = function(unit, object)
	local hide = BlizzardFrameHide[unit]
	if hide then hide() end

	if unit == "pet" then
		object:RegisterEvent"UNIT_HAPPINESS"
	elseif unit == "target" then
		object:RegisterEvent"PLAYER_TARGET_CHANGED"
	elseif unit == "focus" then
		object:RegisterEvent"PLAYER_FOCUS_CHANGED"
	elseif unit == "mouseover" then
		object:RegisterEvent"UPDATE_MOUSEOVER_UNIT"
	elseif unit:match"target" then
		object:SetScript("OnUpdate", OnTargetUpdate)
	end
end

local initObject = function(object, unit, style)
	unit = unit or object.unit
	style = style or object:GetParent().style
	for k, v in pairs(handlers) do
		object[k] = v
	end

	style:setup(object, unit)

	if(not InCombatLockdown()) then
		local width, height, scale = style["initial-width"], style["initial-height"], style["initial-scale"]
		if(width) then object:SetWidth(width) end
		if(height) then object:SetHeight(height) end
		if(scale) then object:SetScale(scale) end
	end
	object:SetAttribute("initial-width", style["initial-width"])
	object:SetAttribute("initial-height", style["initial-height"])
	object:SetAttribute("initial-scale", style["initial-scale"])

	object:SetAttribute("*type1", "target")
	for name, handler in pairs(scripts) do
		object:SetScript(name, handler)
	end
	object:SetScript("OnShow", object.FullUpdate)

	object:RegisterEvent"PLAYER_ENTERING_WORLD"
	object:RegisterEvent"UNIT_CLASSIFICATION_CHANGED"
	object:RegisterEvent"UNIT_DYNAMIC_FLAGS"
	object:RegisterEvent"UNIT_FLAGS"
	object:RegisterEvent"UNIT_LEVEL"

	-- We might want to go deeper then the first level of the table, but there is honestly
	-- nothing preventing us from just placing all the interesting vars at the first level
	-- of it.
	for subType in pairs(object) do
		local data = subTypes[subType]
		local t_data = type(data)
		if t_data == "string" then
			object:RegisterEvent(data)
		elseif t_data == "table" then
			for _, event in ipairs(data) do
				object:RegisterEvent(event)
			end
		elseif t_data == "function" then
			data(object, unit)
		end
	end

	for _, func in ipairs(callback) do
		func(object)
	end

	ClickCastFrames = ClickCastFrames or {}
	ClickCastFrames[object] = true

	objects[object] = true
end

function oUF:RegisterInitCallback(func)
	table.insert(callback, func)
end

function oUF:Spawn(unit, name, style, isPet)
	if(not unit) then return error("Bad argument #1 to 'Spawn' (string expected, got %s)", type(unit)) end
	if(not style) then return error("Unable to create frame. No styles have been registered.") end

	local object
	if unit == "header" then
		local template
		if isPet then
			template = "SecureGroupPetHeaderTemplate"
		else
			-- Yes, I know.
			HandleUnit"party"
			template = "SecureGroupHeaderTemplate"
		end

		local header = CreateFrame("Frame", name, UIParent, template)
		header:SetAttribute("template", "SecureUnitButtonTemplate")
		header.initialConfigFunction = initObject
		header.style = style

		return header
	else
		object = CreateFrame("Button", name, UIParent, "SecureUnitButtonTemplate")
		object:SetAttribute("unit", unit)
		object.unit = unit
		object.id = unit:match"^.-(%d+)"

		initObject(object, unit, style)
		HandleUnit(unit, object)
		RegisterUnitWatch(object)

		if UnitExists(unit) then
			object:FullUpdate()
		end
	end

	return object
end

--[[
--:FullUpdate()
--	Notes:
--		- Does a full update of all elements on the object.
--]]
function handlers:FullUpdate(event)
	local unit = self.unit
	if(not UnitExists(unit)) then return end

	for key, data in pairs(updateTypes) do
		if self[key] then
			if type(data) == "string" and  self:IsEventRegistered(data) then
				self[data](self, event, unit)
			end
		end
	end
end

function handlers:FullUpdateUnit(event, unit)
	if unit == self.unit then
		self:FullUpdate(event)
	end
end


handlers.PLAYER_ENTERING_WORLD = handlers.FullUpdate
handlers.PLAYER_TARGET_CHANGED = handlers.FullUpdate
handlers.PLAYER_FOCUS_CHANGED = handlers.FullUpdate
handlers.UPDATE_MOUSEOVER_UNIT = handlers.FullUpdate

handlers.UNIT_CLASSIFICATION_CHANGED = handlers.FullUpdateUnit
handlers.UNIT_DYNAMIC_FLAGS = handlers.FullUpdateUnit
handlers.UNIT_FLAGS = handlers.FullUpdateUnit
handlers.UNIT_LEVEL = handlers.FullUpdateUnit

--[[ My 8-ball tells me we'll need this one later on.
local ColorGradient = function(perc, r1, g1, b1, r2, g2, b2, r3, g3, b3)
	if perc >= 1 then
		return r3, g3, b3
	elseif perc <= 0 then
		return r1, g1, b1
	end

	local segment, relperc = math_modf(perc*(3-1))
	local offset = (segment*3)+1

	if(offset == 1) then
		return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc
	end

	return r2 + (r3-r2)*relperc, g2 + (g3-g2)*relperc, b2 + (b3-b2)*relperc
end]]


function handlers:UNIT_HAPPINESS(event, unit)
	if(self.unit ~= unit) then return end

	if(self:IsEventRegistered"UNIT_HEALTH") then self:UNIT_HEALTH(event, unit) end
	if(self:IsEventRegistered"UNIT_MANA") then self:UNIT_MANA(event, unit) end
end

oUF.version = ver
oUF.units = units
oUF.subTypes = subTypes
oUF.updateTypes = updateTypes
oUF.colors = colors
oUF.objects = objects
oUF.handlers = handlers

_G.oUF = oUF
